package model.ray;

import javax.vecmath.Point3d;
import javax.vecmath.Vector3d;

import lib.common.Constants;
import model.primitive.IntersectionData;

public class Bouncer {
	
	/**
	 * @param incident Rayo incidente a la primitiva
	 * @param ipoint Punto de interseccion
	 * @param normal Normal al punto de interseccion
	 * @param p Primitiva con la que intersecto
	 * @param rindex indices de refraccion, el primero es el del medio externo, el segundo el del medio entrante
	 * @return Rayo de refraccion
	 */
	public static Ray refract(IntersectionData state) {
		Vector3d normal, refractedRay;
		double refractionIndex, n, cosI, cosT2;
		boolean inside;
		Point3d src;
		
		// Si no permite refraccion el material...
		if (state.primitive.getShader().getRefractionConstant() <= 0) {
			return null;
		}
		
		// Se construye el rayo de refraccion a partir del rayo de interseccion
		refractedRay = state.intersectRay.getDirection();
		
		// Obtiene el indice de refraccion del objeto
		refractionIndex = state.primitive.getShader().getRefractionConstant();

		//Ratio de objecto / air
		n = Constants.airRefractIndex / refractionIndex;

		
		// Obtiene la normal del punto en el que se esta refractando el rayo
		normal = state.getNormal();
		inside = normal.dot(refractedRay) > 0;
		
		// Si el rayo se origino en el interior del objeto, invierte el ratio
		// y la normal para que el rayo refractado no vuelva a re entrar el objeto
		if (inside) {
			
			n = refractionIndex / Constants.airRefractIndex;
			normal.scale(-1);
		}

		
		// coseno del angulo formado entre la normal y el rayo incidente
		cosI = -1.0 * normal.dot(state.intersectRay.getDirection());
		
		cosT2 = 1.0 - n * n * (1.0 - cosI * cosI);
		
		// si cosT2 < 0, hubo reflejo interno total (angulo del rayo refractado > 90 y < 270)
		if (cosT2 < 0)
			return null;
				
		// Construye el rayo refractado
		
		refractedRay.scale(n);
		normal.scale(n * cosI - Math.sqrt(cosT2));
		refractedRay.add(normal);
		
		src = state.hitPoint;
		return new Ray(displaceOrigin(src, refractedRay), refractedRay);
	}
	
	public static Ray reflectRay(IntersectionData state) {
		Ray reflection;
		double refl;
		
		refl = state.primitive.getShader().getReflectionConstant();
		reflection = null;
		
		if (refl > 0.0f) {
			Vector3d inter, r, n; // vector que intersecto a la primitiva
			Point3d src;
			
			r = new Vector3d();
			
			inter = state.intersectRay.getDirection();
			n = state.primitive.getNormal(state.hitPoint);
			
			n.scale(-1*n.dot(inter) * 2.0);
			r.add(inter, n);
			
			src = state.hitPoint;
			src = displaceOrigin(src, r);
			reflection = new Ray(src, r);
		}
		
		return reflection;
	}
	
	/**
	 * Desplaza el origen del rayo un epsilon sobre la direccion
	 * del vector directory del rayo, para evitar self-intersection
	 * al refractar un rayo sobre un punto de interseccion con una primitiva
	 * @param dir Vector direccion
	 * @return punto de origen del rayo
	 */
	
	public static Point3d displaceOrigin(Point3d origin, Vector3d dir) {
		Point3d p;
		
		p = new Point3d();
		
		p.x = origin.x + dir.x * Constants.EPSILON;
		p.y = origin.y + dir.y * Constants.EPSILON;
		p.z = origin.z + dir.z * Constants.EPSILON;
		
		return p;
	}
}
